package data.render;

import java.awt.Color;

import control.profiler.Profiler;
import control.render.RenderSetup;
import data.base.Material;
import data.base.PointLight;
import data.base.RenderColor;
import data.base.Vector3;
import data.complex.Boundary;
import data.complex.Intersection;
import data.complex.Ray;
import data.complex.Triangle;
import data.complex.VoxelSpace;
import data.hierarchy.Mesh;
import data.hierarchy.TriangleGroup;
import data.hierarchy.World;

public class PathTraceRenderMethod implements IRenderMethod{
  private World world;
  private VoxelSpace vs;
  private RenderSetup renderSetup;
  
  public PathTraceRenderMethod(World world, RenderSetup renderSetup){
    this.world = world;
    this.renderSetup = renderSetup;
    Profiler.getInstance().record("Initializing Voxelspace...");
    vs = new VoxelSpace(new Boundary(world));
    refresh();
    Profiler.getInstance().putMessage("Vertices: " + vs.getVertexCount());
    Profiler.getInstance().putMessage("Triangles: " + vs.getTriangleCount());
    Profiler.getInstance().display();
  }
  
  public void refresh(){
    for(Mesh m : world.getMeshes())
      for(TriangleGroup tg : m)
        for(Triangle t : tg)
          vs.registerTriangle(t);
  }

  @Override
  public Color getPixel(Ray ray) {
    RenderColor c = getPixel(ray, renderSetup.getTraceDepth(), null);
    return c.getColor();
  }  
  
  public RenderColor getPixel(Ray ray, int iterationsLeft, Triangle toDiscard){
    RenderColor rayColor = new RenderColor(0);
    iterationsLeft--;      
    vs.calculateFirstTriangleIntersection(ray, Double.MAX_VALUE, toDiscard);
    if(ray.getIntersection().isIntersecting()){
      Intersection intersection = ray.getIntersection();
      Material material = intersection.getTriangle().getGroup().getMaterial();
      RenderColor matColor = material.getRenderColorAt(intersection.getTextureCoordinate());
      
      RenderColor phong, specular;
      
      for(PointLight light : world.getPointLights()){
        Ray toLight = new Ray(ray.getIntersection());
        toLight.setTarget(light);
        double maxDist = toLight.getLength();
        vs.calculateFirstTriangleIntersection(toLight, maxDist, intersection.getTriangle());
        if(!toLight.getIntersection().isIntersecting()){            
          Vector3 toOtherPosition = new Vector3(intersection, light);
          toOtherPosition.normalize();
          
          phong = light.getRenderColor();
          phong.multiplyColor(matColor);
          phong.multiplyBrightness(getPhongFactor(intersection, toOtherPosition) * 0.5);
          
          specular = light.getRenderColor();
          specular.multiplyBrightness(getSpecularFactor(intersection, toOtherPosition, ray.getDirection()));
          
          rayColor.addColor(phong);
          rayColor.addColor(specular);
        }
      }
      if(iterationsLeft > 0){        
        RenderColor surrounding = new RenderColor(0);
        
        for(int i = 0; i < renderSetup.getRaysPerIntersection(); i++){
          Vector3 randomDirection = new Vector3(false);

          if(intersection.getTriangle().getNormal(true).getDotProduct(randomDirection) < 0){
            randomDirection.multiplicate(-1);
          }
          Ray newRay = new Ray(intersection);
          newRay.setDirection(randomDirection);
          
          RenderColor newRayColor = getPixel(newRay, iterationsLeft, intersection.getTriangle());
          
          phong = newRayColor.clone();
          phong.multiplyColor(matColor);
          phong.multiplyBrightness(getPhongFactor(intersection, randomDirection));
          
          specular = newRayColor.clone();
          specular.multiplyBrightness(getSpecularFactor(intersection, randomDirection, ray.getDirection()));
          
          surrounding.addColor(phong);
          surrounding.addColor(specular);
        }
        //TODO 0.2??
        surrounding.multiplyBrightness(1d / renderSetup.getRaysPerIntersection());
        rayColor.addColor(surrounding);
      }
    }
    else{
      rayColor = new RenderColor(renderSetup.getEnvironment());
    }
    return rayColor;
  }
  
  private double getPhongFactor(Intersection i, Vector3 toOtherPosition){
    Vector3 toOther = toOtherPosition.clone();
    toOther.normalize();
    return Math.max(0, toOther.getDotProduct(i.getNormal()));
  }
  
  private double getSpecularFactor(Intersection i, Vector3 toOtherPosition, Vector3 toIntersection){
    Vector3 reflected = toIntersection.clone();
    reflected.normalize();
    reflected.reflect(i.getNormal());
    
    double val = reflected.getDotProduct(toOtherPosition);
    
    //TODO 32 -> specular-size
    if(val > 0)
      return Math.pow(val, 32);
    else
      return 0;
  }
  
  public VoxelSpace getVoxelSpace(){
    return vs;
  }
}
